
package org.sopt.dev.ImgKnight.frontier.Config;

import java.io.IOException;
import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.PushbackInputStream;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;

import jxl.Cell;
import jxl.Sheet;
import jxl.Workbook;
import jxl.read.biff.BiffException;

import org.apache.log4j.Logger;

public class FrontierConfig {
	
	public String ServiceName = null;
	
	public int MaxCrawlerConnectionSize = -1;
	
	public int ServicePort = -1;
	
	public int ControllerPort = -1;
	
	public long PolitenessTimeMillis = -1;
	
	public int UrlPickerDelayMilliSec = -1;
	
	public int HighPrioSiteHashTableHighWaterMark = -1;
	
	public int SiteMapCacheHighWaterMark = -1;
	
	public int SiteMapHashTableLowWaterMark = -1;
	
	public int SiteMapHashTableFetchSize = -1;
	
	public int FPdocsHighWaterMark = -1;
	
	public int ObjectDecoderMaxSize = -1;
	
	public String GeoLocationDataBase = null;
	
	public int GeoLocationFilterDefaultAction = -1;
	
	private List<String> AllowCountryCode = null;
	
	private List<String> DenyCountryCode = null;

	public int UrlPatternFilterDefaultAction = -1;
	
	private List<String> AllowUrlPatterns = null;
	
	private List<String> DenyUrlPatterns = null;
	
	private List<String> ExclusiveUrls = null;
	
	private List<String> HighPrioKeyword = null;
	
	private List<String> seedUrl = null;
	
	public boolean FetchExternalUrl = false;
	
	private Logger serviceLogger = null;
	
	public FrontierConfig() {
		serviceLogger = Logger.getLogger("frontier.service");
	}

	public boolean BuildConfig(File file) throws IOException, FileNotFoundException  {
		if( !isValidFilePath(file) )
			throw new FileNotFoundException("Could not open/read file: " + file.getAbsolutePath());
		
		FileInputStream 	fs = new FileInputStream(file);
		BufferedInputStream bs = new BufferedInputStream(fs);
		PushbackInputStream ps = new PushbackInputStream(bs);
		
		while( readLine(ps) ) {
			if( key != null && value != null ) {
				if( key.equalsIgnoreCase("ServiceName") ) {
					ServiceName = value;
					serviceLogger.debug("ServiceName: " + ServiceName);
				}
				else if( key.equalsIgnoreCase("PolitenessTimeMillis") ) {
					PolitenessTimeMillis = Integer.parseInt(value);
					serviceLogger.debug("PolitenessTimeMillis: " + PolitenessTimeMillis);
				}
				else if( key.equalsIgnoreCase("UrlPickerDelayMilliSec") ) {
					UrlPickerDelayMilliSec = Integer.parseInt(value);
					serviceLogger.debug("UrlPickerDelayMilliSec: " + UrlPickerDelayMilliSec);
				}
				else if( key.equalsIgnoreCase("HighPrioSiteHashTableHighWaterMark") ) {
					HighPrioSiteHashTableHighWaterMark = Integer.parseInt(value);
					serviceLogger.debug("HighPrioSiteHashTableHighWaterMark: " + HighPrioSiteHashTableHighWaterMark);
				}
				else if( key.equalsIgnoreCase("SiteMapCacheHighWaterMark") ) {
					SiteMapCacheHighWaterMark = Integer.parseInt(value);
					serviceLogger.debug("SiteMapCacheHighWaterMark: " + SiteMapCacheHighWaterMark);
				}
				else if( key.equalsIgnoreCase("SiteMapHashTableLowWaterMark") ) {
					SiteMapHashTableLowWaterMark = Integer.parseInt(value);
					serviceLogger.debug("SiteMapHashTableLowWaterMark: " + SiteMapHashTableLowWaterMark);
				}
				else if( key.equalsIgnoreCase("SiteMapHashTableFetchSize") ) {
					SiteMapHashTableFetchSize = Integer.parseInt(value);
					serviceLogger.debug("SiteMapHashTableFetchSize: " + SiteMapHashTableFetchSize);
				}
				else if( key.equalsIgnoreCase("FPdocsHighWaterMark") ) {
					FPdocsHighWaterMark = Integer.parseInt(value);
					serviceLogger.debug("FPdocsHighWaterMark: " + FPdocsHighWaterMark);
				}
				else if( key.equalsIgnoreCase("ObjectDecoderMaxSize") ) {
					ObjectDecoderMaxSize = Integer.parseInt(value);
					serviceLogger.debug("ObjectDecoderMaxSize: " + ObjectDecoderMaxSize);
				}
				else if( key.equalsIgnoreCase("MaxCrawlerConnectionSize") ) {
					MaxCrawlerConnectionSize = Integer.parseInt(value);
					serviceLogger.debug("MaxCrawlerConnectionSize: " + MaxCrawlerConnectionSize);
				}
				else if( key.equalsIgnoreCase("ServicePort") ) {
					ServicePort = Integer.parseInt(value);
					serviceLogger.debug("ServicePort: " + ServicePort);
				}
				else if( key.equalsIgnoreCase("ControllerPort") ) {
					ControllerPort = Integer.parseInt(value);
					serviceLogger.debug("ControllerPort: " + ControllerPort);
				}
				if( key.equalsIgnoreCase("GeoLocationDataBase") ) {
					GeoLocationDataBase = value;
					serviceLogger.debug("GeoLocationDataBase: " + GeoLocationDataBase);
				}
				else if( key.equalsIgnoreCase("GeoLocationFilterDefaultAction") ) {
					if( value.equalsIgnoreCase("allow") ) {
						GeoLocationFilterDefaultAction = 1;
						serviceLogger.debug("GeoLocationFilterDefaultAction: Allow");
					}
					else {
						GeoLocationFilterDefaultAction = 0;
						serviceLogger.debug("GeoLocationFilterDefaultAction: Deny");
					}
				}
				else if( key.equalsIgnoreCase("AllowCountryCode") ) {
					StringTokenizer tokenizer = new StringTokenizer(value);
					AllowCountryCode = new Vector<String>(tokenizer.countTokens());
					while( tokenizer.hasMoreTokens() ) {
						String token = tokenizer.nextToken();
						AllowCountryCode.add(token);
						serviceLogger.debug("AllowCountryCode: " + token);
					}
				}
				else if( key.equalsIgnoreCase("DenyCountryCode") ) {
					StringTokenizer tokenizer = new StringTokenizer(value);
					DenyCountryCode = new Vector<String>(tokenizer.countTokens());
					while( tokenizer.hasMoreTokens() ) {
						String token = tokenizer.nextToken();
						DenyCountryCode.add(token);
						serviceLogger.debug("DenyCountryCode: " + token);
					}
				}
				else if( key.equalsIgnoreCase("UrlPatternFilterDefaultAction") ) {
					if( value.equalsIgnoreCase("allow") ) {
						UrlPatternFilterDefaultAction = 1;
						serviceLogger.debug("UrlPatternFilterDefaultAction: Allow");
					}
					else {
						UrlPatternFilterDefaultAction = 0;
						serviceLogger.debug("UrlPatternFilterDefaultAction: Deny");
					}
				}
				else if( key.equalsIgnoreCase("AllowUrlPatterns") ) {
					StringTokenizer tokenizer = new StringTokenizer(value);
					AllowUrlPatterns = new Vector<String>(tokenizer.countTokens());
					while( tokenizer.hasMoreTokens() ) {
						String token = tokenizer.nextToken();
						AllowUrlPatterns.add(token.toLowerCase());
						serviceLogger.debug("AllowUrlPatterns: " + token);
					}
				}
				else if( key.equalsIgnoreCase("DenyUrlPatterns") ) {
					StringTokenizer tokenizer = new StringTokenizer(value);
					DenyUrlPatterns = new Vector<String>(tokenizer.countTokens());
					while( tokenizer.hasMoreTokens() ) {
						String token = tokenizer.nextToken();
						DenyUrlPatterns.add(token.toLowerCase());
						serviceLogger.debug("DenyUrlPatterns: " + token);
					}
				}
				else if( key.equalsIgnoreCase("ExclusiveUrlListFile") ) {
					ExclusiveUrls = new LinkedList<String>();
					if( parseXls(value, ExclusiveUrls) ) {
						for(String url : ExclusiveUrls) {
							serviceLogger.debug("ExclusiveUrl: " + url);
						}
					}
					else {
						throw new IOException("Could not parse file: " + value);
					}
				}
				else if( key.equalsIgnoreCase("HighPrioKeyword") ) {
					StringTokenizer tokenizer = new StringTokenizer(value);
					HighPrioKeyword = new Vector<String>(tokenizer.countTokens());
					while( tokenizer.hasMoreTokens() ) {
						String token = tokenizer.nextToken();
						HighPrioKeyword.add(token.toLowerCase());
						serviceLogger.debug("HighPrioKeyword: " + token);
					}
				}
				else if( key.equalsIgnoreCase("SeedFile") ) {
					seedUrl = new LinkedList<String>();
					if( parseXls(value, seedUrl) ) {
						for(String url : seedUrl) {
							serviceLogger.debug("SeedUrl: " + url);
						}
					}
					else {
						throw new IOException("Could not parse file: " + value);
					}
				}
				else if( key.equalsIgnoreCase("FetchExternalUrl") ) {
					if( value.equalsIgnoreCase("true") )
						FetchExternalUrl = true;
					else
						FetchExternalUrl = false;
					serviceLogger.debug("FetchExternalUrl: " + FetchExternalUrl);
				}
			}
		}
		ps.close();
		return true;
	}
	
	private boolean parseXls(String filePath, List<String> result) {
		try {
			Workbook wk = Workbook.getWorkbook(new File(filePath));
			Sheet sheet = wk.getSheet(0);
			Cell entryCell = sheet.getCell(0, 0);
			int RowSize = Integer.parseInt(entryCell.getContents());
			Cell urlCell = null;
			for(int i=1; i<=RowSize; i++) {
				urlCell = sheet.getCell(0, i);
				result.add(urlCell.getContents());
			}
			if( RowSize == result.size() )
				return true;
		} catch (BiffException e) { } 
		catch (IOException e) { 
		}
		return false;
	}
	
	boolean isValidFilePath(File file) {
		return (file.isFile() && file.canRead());
	}
	
	static final int MAX_LINE_LENGTH = 1024;
	String key, value;
	StringBuffer linebuf = new StringBuffer();
	boolean readLine(PushbackInputStream in) throws IOException {
		key 	= null;
		value 	= null;
		linebuf.setLength(0);
		
		int c;
		int n = 0;
		boolean saw_eoln = false;
		while( true ) {
			c = in.read();
			if( c == -1 )
				break;
			else if( c == '\r' || c == '\n' )
				saw_eoln = true;
			else if( saw_eoln ) {
				in.unread(c);
				break;
			}
			else {
				linebuf.append((char)c);
			}
			++n;
			if( n == MAX_LINE_LENGTH )
				break;
		}
		if( n == 0 )
			return false;

		String line = linebuf.toString();
		line.trim();
		if( line.startsWith("#") )
			return true;
		int colon = line.indexOf (':');
		if( colon == -1 ) {
			key 	= null;
			value 	= null;
		}
		else {
			key  = line.substring(0, colon).trim();
			value = line.substring(colon+1).trim();
		}		 
		return true;
	}
	
	public String [] getSeedUrl() {
		synchronized (seedUrl) {
			String [] seed = new String[seedUrl.size()];
			seedUrl.toArray(seed);
			return seed;
		}
	}
	
	public String [] getAllowUrlPattern() {
		synchronized (AllowUrlPatterns) {
			String [] allowUrlPatterns = new String[AllowUrlPatterns.size()];
			AllowUrlPatterns.toArray(allowUrlPatterns);
			return allowUrlPatterns;
		}
	}
	
	public String [] getDenyUrlPattern() {
		synchronized (DenyUrlPatterns) {
			String [] denyUrlPatterns = new String[DenyUrlPatterns.size()];
			DenyUrlPatterns.toArray(denyUrlPatterns);
			return denyUrlPatterns;
		}
	}
	
	public void addAllowCountryCode(String countryCode) {
		synchronized (AllowCountryCode) {
			AllowCountryCode.add(countryCode);
		}
	}
	
	public void addDenyCountryCode(String countryCode) {
		synchronized (DenyCountryCode) {
			DenyCountryCode.add(countryCode);
		}
	}
	
	public void addAllowUrlPatterns(String patterns) {
		synchronized (AllowUrlPatterns) {
			AllowUrlPatterns.add(patterns);
		}
	}
	
	public void addDenyUrlPatterns(String patterns) {
		synchronized (DenyUrlPatterns) {
			DenyUrlPatterns.add(patterns);
		}
	}
	
	public void addHighPrioKeyword(String keyword) {
		synchronized (HighPrioKeyword) {
			HighPrioKeyword.add(keyword);
		}
	}
	
	public void addSeedUrl(String seed) {
		synchronized (seedUrl) {
			seedUrl.add(seed);
		}
	}
	
	public boolean containsAllowCountryCode(String countryCode) {
		synchronized (AllowCountryCode) {
			for(String code : AllowCountryCode) {
				if( code.equals(countryCode) ) {
					return true;
				}
			}
			return false;
		}
	}
	
	public boolean containsDenyCountryCode(String countryCode) {
		synchronized (DenyCountryCode) {
			for(String code : DenyCountryCode) {
				if( code.equals(countryCode) ) {
					return true;
				}
			}
			return false;	
		}
	}

	public boolean containsAllowUrlPatterns(String pattern) {
		synchronized (AllowUrlPatterns) {
			for(String p : AllowUrlPatterns) {
				if( pattern.contains(p.toLowerCase()) ) {
					return true;
				}
			}
			return false;
		}
	}

	public boolean containsDenyUrlPatterns(String pattern) {
		synchronized (DenyUrlPatterns) {
			for(String p : DenyUrlPatterns) {
				if( pattern.contains(p.toLowerCase()) ) {
					return true;
				}
			}
			return false;
		}
	}
	
	public boolean containsExclusiveUrls(String hostName) {
		synchronized (ExclusiveUrls) {
			for(String exclusive : ExclusiveUrls) {
				if( exclusive.contains(hostName) ) {
					return true;
				}
			}
			return false;
		}
	}

	public boolean containsHighPrioKeyword(String hostUrl) {
		synchronized (HighPrioKeyword) {
			for(String keyword : HighPrioKeyword) {
				if( hostUrl.contains(keyword.toLowerCase()) ) {
					return true;
				}
			}
			return false;
		}
	}
	
	public boolean containsSeedUrl(String hostUrl) {
		synchronized (seedUrl) {
			for(String seed : seedUrl) {
				if( seed.contains(hostUrl) )
					return true;
			}
			return false;
		}
	}
	
	public boolean equalsSeedUrl(String keyword) {
		synchronized (seedUrl) {
			for(String seed : seedUrl) {
				StringTokenizer tokenizer = new StringTokenizer(seed, ".");
				while( tokenizer.hasMoreTokens() ) {
					if( tokenizer.nextToken().equals(keyword) )
						return true;
				}
			}
			return false;
		}
	}
}
